package sundun.sharding.execution;


import com.baomidou.mybatisplus.toolkit.PluginUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import sundun.sharding.config.TableConfiguration;
import sundun.sharding.factory.ExecutionFactory;
import sundun.sharding.factory.SqlSessionFactoryWrapper;

import java.text.DateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Spring bean注入 Interceptor 处理
 *
 * @author: zxl
 * @date: 2024/8/11 09:22
 */

public class Executions {


    private static final Pattern TABLE_NAME_PATTERN = Pattern.compile(
            "\\bfrom\\s+([a-zA-Z0-9_]+)\\b|" +  // 匹配 FROM 后面的表名
                    "\\bjoin\\s+([a-zA-Z0-9_]+)\\b|" +  // 匹配 JOIN 后面的表名
                    "\\binsert\\s+into\\s+([a-zA-Z0-9_]+)\\b|" +  // 匹配 INSERT INTO 后面的表名
                    "\\bdelete\\s+from\\s+([a-zA-Z0-9_]+)\\b|" +  // 匹配 DELETE FROM 后面的表名
                    "\\bupdate\\s+([a-zA-Z0-9_]+)\\b",  // 匹配 UPDATE 后面的表名
            Pattern.CASE_INSENSITIVE
    ); private ExecutionFactory executionFactory;
    private SqlSessionFactoryWrapper sqlSessionFactoryWrapper;

    public Executions(SqlSessionFactoryWrapper sqlSessionFactoryWrapper) {
        this.sqlSessionFactoryWrapper = sqlSessionFactoryWrapper;
        try {
            this.executionFactory = new ExecutionFactory(sqlSessionFactoryWrapper,sqlSessionFactoryWrapper.getTableConfigurations());
        } catch (ClassNotFoundException e) {
            //防御性容错
        }
    }

    public Object proxyExecute(Invocation invocation) throws Exception {
        // 多个拦截器拦截同一个对象时，第二个拦截器获取的target对象为Proxy不是StatementHandler，此处改为用PluginUtils获取真正的target对象
        StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
        // 获取 SQL 语句
        BoundSql boundSql = mappedStatement.getBoundSql(metaStatementHandler.getValue("delegate.parameterHandler.parameterObject"));
        String sql = boundSql.getSql().toLowerCase();
//        String classPath = mappedStatement.getId();
        //获取分片表的mapper文件包路径 例如：sundun.sharding.mapper.ImsYesterdayJqxxDao
//        String interfaceMapper = classPath.substring(0, classPath.lastIndexOf("."));
        // 提取 SQL 语句中的表名
        List<String> tableNames = extractTableNames(sql);
        Optional<TableConfiguration> first = tableNames.stream().map(s -> sqlSessionFactoryWrapper.getTableConfigurations().get(s))
                .filter(Objects::nonNull).findFirst();

        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
//        TableConfiguration tableConfiguration = sqlSessionFactoryWrapper.getTableConfigurations().get(interfaceMapper);
        //表未做配置放行
        if (!first.isPresent() || executionFactory == null) {
            return invocation.proceed();
        }
        //获取执行器
        ExecutionStrategy executionStrategy = executionFactory.getExecutionStrategy(sqlCommandType);
        if (Objects.isNull(executionStrategy)){
            return invocation.proceed();
        }
        return executionStrategy.execute(invocation,first.get(),metaStatementHandler,mappedStatement);
    }


    /**
     * 解析sql语句中表名称
     * @param sql
     * @return
     */
    private List<String> extractTableNames(String sql) {
        Set<String> tableNames = new HashSet<>();
        Matcher matcher = TABLE_NAME_PATTERN.matcher(sql);

        while (matcher.find()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                if (matcher.group(i) != null) {
                    tableNames.add(matcher.group(i));
                }
            }
        }
        return new ArrayList<>(tableNames);
    }

    /**
     * 获取带参SQL
     *
     * @param configuration 节点的配置
     * @param boundSql      封装myBatis最终产生的sql类
     * @return
     */
    public static String getSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();//获取参数
        List<ParameterMapping> parameterMappings = boundSql
                .getParameterMappings();
        //sql语句中多个空格都用一个空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            //获取类型处理器注册器，类型处理器的功能是进行java类型和数据库类型的转换。如果根据parameterObject.getClass(）可以找到对应的类型，则替换
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                //MetaObject主要是封装了originalObject对象，提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        //该分支是动态sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        //打印出缺失，提醒该参数缺失并防止错位
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }


    /**
     * 对SQL进行格式化
     *
     * @param obj
     * @return
     */
    private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }


}
